---
title: 注解配置
sidebar:
  order: 2
---

import { LinkCard } from '@astrojs/starlight/components';
import Badge from "@/components/Badge.astro";

本文详细介绍了 MyBatisPlus 注解的用法及属性，提供了源码链接以便深入理解。欢迎通过下方链接查看注解类的源码。

<LinkCard
  title="MyBatis-Plus Annotation 源码"
  description="源码中包含较为完整的注释，可以查看源码了解功能。"
  href="https://github.com/baomidou/mybatis-plus/tree/3.0/mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation"
/>

## @TableName

该注解用于指定实体类对应的数据库表名。当实体类名与数据库表名不一致，或者实体类名不是数据库表名的驼峰写法时，您需要使用这个注解来明确指定表名。

```java {1}
@TableName("sys_user")
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
```

### value

<p>
    **类型：** `String` <br/>
    **默认值：** `""`
</p>

指定实体类对应的数据库表名。如果实体类名与表名不一致，使用这个属性来指定正确的表名。

### schema

<p>
    **类型：** `String` <br/>
    **默认值：** `""`
</p>

指定数据库的 Schema 名称。通常情况下，如果你的数据库没有使用 Schema 来组织表，这个属性可以不填写。

### keepGlobalPrefix

<p>
    **类型：** `boolean` <br/>
    **默认值：** `false`
</p>

当全局配置了 tablePrefix 时，这个属性决定是否保持使用全局的表前缀。如果设置为 true，即使注解中指定了表名，也会自动加上全局的表前缀。

### resultMap

<p>
    **类型：** `String` <br/>
    **默认值：** `""`
</p>

指定在 XML 中定义的 ResultMap 的 ID，用于将查询结果映射到特定类型的实体类对象。

### autoResultMap

<p>
    **类型：** `boolean` <br/>
    **默认值：** `false`
</p>

是否自动构建 resultMap。如果已经设置了 resultMap，这个属性不会生效。

:::note
MyBatis-Plus 会自动构建一个 resultMap 并注入到 MyBatis 中。但是，一旦注入完成，生成的内容就是静态的，类似于 XML 配置中的内容。在使用与 resultMap 相关的操作时，请注意 typeHandler 的处理。

MyBatis 只支持将 typeHandler 写在两个地方：

- 定义在 resultMap 中，作用于查询结果的封装。
- 定义在 insert 和 update 语句的 `#{property}` 中的 property 后面，例如：`#{property,typehandler=xxx.xxx.xxx}`，并且只作用于当前设置的值。

除了以上两种直接指定 typeHandler 的形式，MyBatis 还有一个全局扫描自定义 typeHandler 包的配置，原理是根据您的属性类型去找其对应的 typeHandler 并使用。
:::

### excludeProperty

<p>
    **类型：** `String[]` <br/>
    **默认值：** `{}` <br/>
    **添加于：** <Badge text="@since 3.3.1" vertical="middle"/>
</p>

指定在映射时需要排除的属性名。这些属性将不会被包含在生成的 SQL 语句中。

## @TableId

该注解用于标记实体类中的主键字段。如果你的主键字段名为 id，你可以省略这个注解。

```java {3}
@TableName("sys_user")
public class User {
    @TableId
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
```

### value

<p>
    **类型：** `String` <br/>
    **默认值：** `""`
</p>

指定数据库表的主键字段名。如果不设置，MyBatis-Plus 将使用实体类中的字段名作为数据库表的主键字段名。

### type

<p>
    **类型：** `Enum` <br/>
    **默认值：** `IdType.NONE`
</p>

指定主键的生成策略。

**IdType 枚举类型定义**

- `IdType.AUTO`：使用数据库自增 ID 作为主键。
- `IdType.NONE`：无特定生成策略，如果全局配置中有 IdType 相关的配置，则会跟随全局配置。
- `IdType.INPUT`：在插入数据前，由用户自行设置主键值。
- `IdType.ASSIGN_ID`：自动分配 `ID`，适用于 `Long`、`Integer`、`String` 类型的主键。默认使用雪花算法通过 `IdentifierGenerator` 的 `nextId` 实现。<Badge text="@since 3.3.0"/>
- `IdType.ASSIGN_UUID`：自动分配 `UUID`，适用于 `String` 类型的主键。默认实现为 `IdentifierGenerator` 的 `nextUUID` 方法。<Badge text="@since 3.3.0"/>

:::note
请注意，已弃用的ID类型（如 `ID_WORKER`, `UUID`, `ID_WORKER_STR`）应避免使用，并使用 `ASSIGN_ID` 或 `ASSIGN_UUID` 代替。这些新的策略提供了更好的灵活性和兼容性。
:::

## @TableField

该注解用于标记实体类中的非主键字段，它告诉 MyBatis-Plus 如何映射实体类字段到数据库表字段。如果你的实体类字段名遵循驼峰命名规则，并且与数据库表字段名一致，你可以省略这个注解。

```java {5}
@TableName("sys_user")
public class User {
    @TableId
    private Long id;
    @TableField("nickname") // 映射到数据库字段 "nickname"
    private String name;
    private Integer age;
    private String email;
}
```

### value

<p>
    **类型：** `String`<br/>
    **默认值：** `""`
</p>
指定数据库中的字段名。如果你的实体类字段名与数据库字段名不同，使用这个属性来指定正确的数据库字段名。

### exist

<p>
    **类型：** `boolean`<br/>
    **默认值：** `true`
</p>
指示这个字段是否存在于数据库表中。如果设置为 false，MyBatis-Plus 在生成 SQL 时会忽略这个字段。

### condition

<p>
    **类型：** `String`<br/>
    **默认值：** `""`
</p>
在执行实体查询（EntityQuery）时，指定字段的条件表达式。这允许你自定义字段在 WHERE 子句中的比较方式。如果该项有值则按设置的值为准，无值则默认为全局的 `%s=#{%s}` 为准。

写法详见 [SqlCondition](https://github.com/baomidou/mybatis-plus/blob/3.0/mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/SqlCondition.java)。

:::note[EntityQuery 解释]
EntityQuery 是指在构建查询条件时，直接使用实体类的字段来设置查询条件，而不是手动编写 SQL 片段。
:::

**示例说明**

假设我们有一个实体类 User，它有 id、name 和 age 三个字段。我们想要查询所有年龄大于 18 岁的用户，我们可以使用 QueryWrapper 来构建这个查询，直接传递 User 实体类实例：

```java
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.SqlCondition;

// 实体类定义
@TableName("sys_user")
public class User {
    @TableId
    private Long id;

    private String name;

    @TableField(condition = "%s > #{%s}") // 自定义 age 字段的条件表达式
    private Integer age;

    private String email;
}

// 使用 EntityQuery 构建查询
public List<User> findUserAgeOver18() {
    // 创建 User 实例，用于设置查询条件
    User queryEntity = new User();
    queryEntity.setAge(18); // 设置 age 字段的值

    // 创建 QueryWrapper 实例，并传递 User 实例
    QueryWrapper<User> queryWrapper = new QueryWrapper<>(queryEntity);

    // 执行查询
    List<User> userList = userMapper.selectList(queryWrapper);

    return userList;
}
```

在这个例子中，我们通过 `@TableField(condition = "%s > #{%s}")` 注解为 age 字段设置了自定义的条件表达式。当构建查询时，我们创建了一个 User 实例，并设置了 age 字段的值为 18。然后，我们使用这个实例来创建 QueryWrapper，MyBatis-Plus 会根据实体类上的注解自动生成相应的 SQL 查询条件。

执行 findUserAgeOver18 方法时，MyBatis-Plus 会生成类似以下的 SQL 语句：

```sql
SELECT * FROM sys_user WHERE age > 18;
```

通过这种方式，condition 属性允许我们自定义字段在查询时的行为，使得查询更加灵活和符合特定需求，同时避免了手动编写 SQL 片段的繁琐。

### update

<p>
    **类型：** `String`<br/>
    **默认值：** `""`
</p>
在执行更新操作时，指定字段在 SET 子句中的表达式。这个属性的优先级高于 el 属性，允许你自定义字段的更新逻辑。

**示例说明**

假设我们有一个实体类 User，其中包含一个 version 字段，我们希望在每次更新用户信息时，自动将 version 字段的值增加 1。我们可以使用 @TableField 注解的 update 属性来实现这个功能：

```java
import com.baomidou.mybatisplus.annotation.TableField;

@TableName("sys_user")
public class User {
    @TableId
    private Long id;

    private String name;

    private Integer age;

    private String email;

    @TableField(update="%s+1") // 自定义更新时的表达式
    private Integer version;
}
```

在这个例子中，`@TableField(update="%s+1")` 注解告诉 MyBatis-Plus，在执行更新操作时，对于 version 字段，应该使用 `version = version + 1` 的表达式。这意味着，每次更新用户信息时，version 字段的值都会自动增加 1。

如果我们执行以下更新操作：

```java
User user = new User();
user.setId(1L);
user.setName("Updated Name");
user.setAge(30);
user.setEmail("updated@example.com");

userMapper.updateById(user);
```

MyBatis-Plus 会自动生成类似以下的 SQL 语句：

```sql
UPDATE sys_user
SET name = 'Updated Name', age = 30, email = 'updated@example.com', version = version + 1
WHERE id = 1;
```

通过这种方式，update 属性允许我们自定义字段在更新时的行为，使得更新操作更加灵活和符合特定需求，同时避免了手动编写 SQL 片段的繁琐。

### insertStrategy

<p>
    **类型：** `Enum`<br/>
    **默认值：** `FieldStrategy.DEFAULT`
</p>

定义在插入新记录时，如何处理字段的值。这个属性允许你控制字段是否应该包含在 INSERT 语句中，以及在什么条件下包含。

**[FieldStrategy](https://github.com/baomidou/mybatis-plus/blob/3.0/mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/FieldStrategy.java) 枚举类型定义**

- FieldStrategy.DEFAULT：遵循全局配置的策略。如果全局配置未指定，默认行为是仅在字段值不为 NULL 时插入该字段。
- FieldStrategy.ALWAYS：总是插入该字段，无论字段值是否为 NULL。
- FieldStrategy.NOT_NULL：仅在字段值不为 NULL 时插入该字段。
- FieldStrategy.NOT_EMPTY：仅在字段值不为空（对于字符串类型）或不为 NULL（对于其他类型）时插入该字段。
- FieldStrategy.NEVER：从不插入该字段，即使字段值不为 NULL。
- FieldStrategy.IGNORED： 忽略判断，效果等同于”ALWAYS” <Badge text="@Deprecated" type="error"/>

**示例说明**

假设我们有一个实体类 User，其中包含一个 nickname 字段，我们希望在插入新用户时，只有当 nickname 不为空时才插入该字段。我们可以使用 @TableField 注解的 insertStrategy 属性来实现这个功能：

```java
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.FieldStrategy;

@TableName("sys_user")
public class User {
    @TableId
    private Long id;

    @TableField(insertStrategy = FieldStrategy.NOT_EMPTY) // 仅在 nickname 不为空时插入
    private String nickname;

    private Integer age;

    private String email;
}
```

在这个例子中，`@TableField(insertStrategy = FieldStrategy.NOT_EMPTY)` 注解告诉 MyBatis-Plus，在插入新用户时，只有当 nickname 字段不为空时才将其包含在 INSERT 语句中。

如果我们执行以下插入操作：

```java
User user = new User();
user.setNickname("John Doe");
user.setAge(25);
user.setEmail("john.doe@example.com");

userMapper.insert(user);
```

MyBatis-Plus 会自动生成类似以下的 SQL 语句：

```sql
INSERT INTO sys_user (nickname, age, email)
VALUES ('John Doe', 25, 'john.doe@example.com');
```

如果 nickname 字段为空，生成的 SQL 将不包含 nickname 字段：

```sql
INSERT INTO sys_user (age, email)
VALUES (25, 'john.doe@example.com');
```

其效果等同于如下 MyBatis 的自定义 XML 配置：

```xml
<mapper namespace="com.example.mapper.UserMapper">

    <!-- 插入用户 -->
    <insert id="insertUser" parameterType="com.example.entity.User">
        INSERT INTO sys_user (
            <if test="nickname != null and nickname != ''">
                nickname,
            </if>
            age,
            email
        ) VALUES (
            <if test="nickname != null and nickname != ''">
                #{nickname},
            </if>
            #{age},
            #{email}
        )
    </insert>

</mapper>
```

通过这种方式，insertStrategy 属性允许我们自定义字段在插入时的行为，使得插入操作更加灵活和符合特定需求，同时避免了手动编写 SQL 片段的繁琐。

### updateStrategy

<p>
    **类型：** `Enum`<br/>
    **默认值：** `FieldStrategy.DEFAULT`
</p>
定义在更新记录时，如何处理字段的值。这个属性允许你控制字段是否应该包含在 UPDATE 语句的 SET 子句中，以及在什么条件下包含。

参见 insertStrategy 属性以获取更多关于 FieldStrategy 枚举类型的详细信息。

**示例说明**

假设我们有一个实体类 User，其中包含一个 nickname 字段，我们希望在更新用户信息时，总是更新 nickname 字段，无论其值是否为空。我们可以使用 @TableField 注解的 updateStrategy 属性来实现这个功能：

```java
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.FieldStrategy;

@TableName("sys_user")
public class User {
    @TableId
    private Long id;

    @TableField(updateStrategy = FieldStrategy.ALWAYS) // 总是更新 nickname，忽略值的检查
    private String nickname;

    private Integer age;

    private String email;
}
```

在这个例子中，`@TableField(updateStrategy = FieldStrategy.ALWAYS)` 注解告诉 MyBatis-Plus，在更新用户信息时，总是将 nickname 字段包含在 UPDATE 语句的 SET 子句中，忽略其值的检查。

如果我们执行以下更新操作：

```java
User user = new User();
user.setId(1L);
user.setNickname("Updated Nickname");
user.setAge(30);
user.setEmail("updated@example.com");

userMapper.updateById(user);
```

MyBatis-Plus 会自动生成类似以下的 SQL 语句：

```sql
UPDATE sys_user
SET nickname = 'Updated Nickname', age = 30, email = 'updated@example.com'
WHERE id = 1;
```

无论 nickname 字段的值是否为空，生成的 SQL 都会包含 nickname 字段。也就是说，即使 nickname 字段的值为空，生成的 SQL 也会更新 nickname 字段为 NULL。

通过这种方式，updateStrategy 属性允许我们自定义字段在更新时的行为，使得更新操作更加灵活和符合特定需求，同时避免了手动编写 SQL 片段的繁琐。

### whereStrategy

<p>
    **类型：** `Enum`<br/>
    **默认值：** `FieldStrategy.DEFAULT`
</p>
定义在生成更新语句的 WHERE 子句时，如何处理字段的值。这个属性允许你控制字段是否应该包含在 WHERE 子句中，以及在什么条件下包含。

参见 insertStrategy 和 updateStrategy 属性以获取更多关于 FieldStrategy 枚举类型的详细信息。

**示例说明**

假设我们有一个实体类 User，其中包含一个 nickname 字段，我们希望在更新用户信息时，只有当 nickname 字段不为空时，才将其作为 WHERE 子句的条件。我们可以使用 @TableField 注解的 whereStrategy 属性来实现这个功能：

```java
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.FieldStrategy;

@TableName("sys_user")
public class User {
    @TableId
    private Long id;

    @TableField(whereStrategy = FieldStrategy.NOT_EMPTY) // 仅在 nickname 不为空时作为 WHERE 条件
    private String nickname;

    private Integer age;

    private String email;
}
```

在这个例子中，`@TableField(whereStrategy = FieldStrategy.NOT_EMPTY)` 注解告诉 MyBatis-Plus，在使用 whereEntity 生成更新语句的 WHERE 子句时，只有当 nickname 字段不为空时，才将其包含在 WHERE 子句中。

如果我们执行以下更新操作：

```java
User user = new User();
user.setEmail("john.doe@example.com");

User whereEntity = new User();
whereEntity.setNickname("John Doe");
whereEntity.setAge(30);

// 使用 whereEntity 方法
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(whereEntity);
userMapper.update(user, updateWrapper);
```

MyBatis-Plus 会自动生成类似以下的 SQL 语句：

```sql
UPDATE sys_user
SET email = 'john.doe@example.com'
WHERE nickname = 'John Doe' AND age = 30;
```

如果 nickname 字段为空，生成的 SQL 将不包含 nickname 字段：

```sql
UPDATE sys_user
SET email = 'john.doe@example.com'
WHERE age = 30;
```

其效果等同于如下 MyBatis 的自定义 XML 配置：

```xml
<mapper namespace="com.example.mapper.UserMapper">

    <!-- 更新用户信息 -->
    <update id="updateUser" parameterType="com.example.entity.User">
        UPDATE sys_user
        SET email = #{email}
        <where>
            <if test="nickname != null and nickname != ''">
                AND nickname = #{nickname}
            </if>
            AND age = #{age}
        </where>
    </update>
    
</mapper>
```

通过这种方式，whereStrategy 属性允许我们自定义字段在 WHERE 子句中的行为，使得更新操作更加灵活和符合特定需求，同时避免了手动编写 SQL 片段的繁琐。参见 insertStrategy 和 updateStrategy 属性以获取更多关于 FieldStrategy 枚举类型的详细信息。

### fill

<p>
    **类型：** `Enum`<br/>
    **默认值：** `FieldFill.DEFAULT`
</p>
字段自动填充策略。该属性用于指定在执行数据库操作（如插入、更新）时，如何自动填充字段的值。通过使用 FieldFill 枚举，可以灵活地控制字段的填充行为。

:::note
此功能需要配合[自动填充字段](/guides/auto-fill-field)功能一起使用。
:::

**[FieldFill](https://github.com/baomidou/mybatis-plus/blob/3.0/mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/FieldFill.java) 枚举类型定义**

- `FieldFill.DEFAULT`：默认不进行填充，依赖于数据库的默认值或手动设置。

- `FieldFill.INSERT`：在插入操作时自动填充字段值。

- `FieldFill.UPDATE`：在更新操作时自动填充字段值。

- `FieldFill.INSERT_UPDATE`：在插入和更新操作时都会自动填充字段值。

**示例说明**

假设有一个 User 实体类，其中包含 createTime 和 updateTime 字段，我们希望在创建用户时自动填充创建时间，在更新用户信息时自动填充更新时间。

```java
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import java.time.LocalDateTime;

@TableName("user")
public class User {
    // 其他字段...

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.UPDATE)
    private LocalDateTime updateTime;

    // 构造函数、getter 和 setter...
}
```

在这个示例中，createTime 字段会在插入操作时自动填充当前时间，而 updateTime 字段会在更新操作时自动填充当前时间。这样，我们就不需要在每次数据库操作时手动设置这些时间字段的值了。

**请注意**，为了使自动填充功能正常工作，您需要在 MyBatis-Plus 的配置中设置相应的自动填充处理器，并且确保在实体类对应的 Mapper 接口中定义了相应的插入和更新方法。

### select

<p>
    **类型：** `boolean`<br/>
    **默认值：** `true`
</p>
指示在执行查询操作时，该字段是否应该包含在 SELECT 语句中。这个属性允许您控制查询结果中包含哪些字段，从而提供更细粒度的数据访问控制。

**详细说明**

- 当 select 属性设置为 true（默认值）时，该字段将包含在查询结果中。

- 当 select 属性设置为 false 时，即使该字段存在于数据库表中，它也不会包含在查询结果中。这在需要保护敏感数据或优化查询性能时非常有用。

**示例说明**

假设有一个 User 实体类，其中包含 password 字段，我们希望在查询用户信息时排除密码字段，以保护用户隐私。

```java
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;

@TableName("user")
public class User {
    // 其他字段...

    @TableField(select = false)
    private String password;

    // 构造函数、getter 和 setter...
}
```

在这个示例中，当执行查询操作时，password 字段不会被包含在 SELECT 语句中，因此不会出现在查询结果中。这样，即使数据库中存储了密码信息，也不会在常规查询中泄露。

请注意，@TableField 注解的 select 属性仅影响 MyBatis-Plus 生成的查询语句，不会影响其他框架或手动编写的 SQL 语句。此外，如果使用了 select = false 的字段，那么在自定义查询或使用其他方式访问该字段时，需要特别注意数据的安全性和完整性。

### keepGlobalFormat

<p>
    **类型：** `boolean`<br/>
    **默认值：** `false`
</p>
指示在处理字段时是否保持使用全局 DbConfig 中定义的 columnFormat 规则。这个属性用于控制字段值在数据库操作中是否应用全局的列格式化规则。

### jdbcType

<p>
    **类型：** `JdbcType`<br/>
    **默认值：** `JdbcType.UNDEFINED`
</p>
JDBC类型，用于指定字段在数据库中的数据类型。这个属性允许您显式地设置字段的数据库类型，以确保与数据库的兼容性，特别是在处理特殊类型或自定义类型时。

**详细说明**

- 当 jdbcType 属性设置为 JdbcType.UNDEFINED（默认值）时，MyBatis-Plus 将根据字段的 Java 类型自动推断其 JDBC 类型。

- 当 jdbcType 属性设置为特定的 JdbcType 枚举值时，该字段将使用指定的 JDBC 类型进行数据库操作。这可以用于解决类型映射问题，或者在需要精确控制数据库类型时使用。

**示例说明**

假设有一个 CustomType 实体类，其中包含一个自定义类型 MyCustomType 的字段，我们希望在数据库中以特定的 JDBC 类型存储。

```java
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import org.apache.ibatis.type.JdbcType;

@TableName("custom_type")
public class CustomType {
    // 其他字段...

    @TableField(value = "my_custom_field", jdbcType = JdbcType.VARCHAR)
    private MyCustomType myCustomField;

    // 构造函数、getter 和 setter...
}
```

在这个示例中，myCustomField 字段将使用 VARCHAR JDBC 类型进行数据库操作。这样，即使 MyCustomType 是一个自定义类型，它也会被转换为 VARCHAR 类型存储在数据库中。

**请注意**，jdbcType 属性仅在特定情况下需要设置，例如当 Java 类型与数据库类型之间存在不明确的映射关系时。在大多数情况下，MyBatis-Plus 能够自动处理类型映射，因此不需要显式设置 jdbcType。此外，jdbcType 属性仅影响 MyBatis-Plus 生成的 SQL 语句，不会影响其他框架或手动编写的 SQL 语句。

### typeHandler

<p>
    **类型：** `Class<? extends TypeHandler>`<br/>
    **默认值：** `UnknownTypeHandler.class`
</p>
类型处理器，用于指定在数据库操作中如何处理特定字段的值。这个属性允许您自定义字段值的转换逻辑，以适应特定的数据类型或业务需求。

**详细说明**

- 当 typeHandler 属性未设置（即使用默认值 UnknownTypeHandler.class）时，MyBatis-Plus 将使用默认的类型处理器来处理字段值。

- 当 typeHandler 属性设置为特定的 TypeHandler 子类时，该字段将使用指定的类型处理器进行数据库操作。这可以用于处理自定义类型、特殊数据格式或非标准的数据库类型。

**示例说明**

假设我们有一个 User 实体类，其中包含一个 birthDate 字段，我们希望使用自定义的类型处理器来处理日期格式，该处理器能够将日期按照特定的格式存储在数据库中。

```java
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.example.myproject.typehandler.CustomDateTypeHandler;
import java.time.LocalDate;

@TableName("user")
public class User {
    // 其他字段...

    @TableField(value = "birth_date", typeHandler = CustomDateTypeHandler.class)
    private LocalDate birthDate;

    // 构造函数、getter 和 setter...
}
```

在这个示例中，birthDate 字段将使用 CustomDateTypeHandler 类型处理器进行数据库操作。这样，无论是在查询时将数据库中的日期值转换为 LocalDate 对象，还是在更新时将 LocalDate 对象的日期值转换为数据库中的特定日期格式，都会使用 CustomDateTypeHandler 来处理。

CustomDateTypeHandler 可能实现如下：

```java
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class CustomDateTypeHandler extends BaseTypeHandler<LocalDate> {
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, LocalDate parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter.format(FORMATTER));
    }

    @Override
    public LocalDate getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String dateString = rs.getString(columnName);
        return (dateString != null) ? LocalDate.parse(dateString, FORMATTER) : null;
    }

    // 实现其他必要的方法...
}
```

在这个自定义类型处理器中，我们定义了一个 DateTimeFormatter 来指定日期的格式，并在 setNonNullParameter 和 getNullableResult 方法中实现了日期值的转换逻辑。

**请注意**，为了使自定义类型处理器生效，您需要确保它在 MyBatis-Plus 的配置中被正确注册，并且能够在运行时被加载。此外，自定义类型处理器的使用应当谨慎，确保其正确性和性能。更多详细信息，请参考 [字段类型处理器](/guides/type-handler) 的内容。

### numericScale

<p>
    **类型：** `String`<br/>
    **默认值：** `""`
</p>
指定小数点后保留的位数，该属性仅在执行 update 操作时生效。它用于控制数值类型字段在更新时的小数精度。

**详细说明**

- 当 numericScale 属性设置为空字符串（默认值）时，字段的小数精度将遵循数据库的默认设置或字段定义时的设置。
- 当 numericScale 属性设置为特定的数值（如 "2"）时，该字段在执行 update 操作时将按照指定的小数位数进行处理。

**示例说明**
假设有一个 Product 实体类，其中包含一个 price 字段，我们希望在更新价格时确保小数点后保留两位。

```java
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;

@TableName("product")
public class Product {
    // 其他字段...

    @TableField(value = "price", numericScale = "2")
    private BigDecimal price;

    // 构造函数、getter 和 setter...
}
```

在这个示例中，price 字段在执行 update 操作时将确保小数点后保留两位。这意味着在更新价格时，无论传入的价格值小数位数是多少，都会被格式化为两位小数。

**请注意**，为了使 numericScale 属性生效，您需要确保数据库支持指定的小数位数，并且在执行 update 操作时，传入的数值类型字段值会被正确处理。此外，numericScale 属性仅影响 MyBatis-Plus 生成的 SQL 语句，不会影响其他框架或手动编写的 SQL 语句。

:::note[关于 numericScale、jdbcType、typeHandler 的说明]

- numericScale 属性仅在 update 操作时生效。
- jdbcType 和 typeHandler 属性如果不与 @TableName 中的 autoResultMap = true 一起使用，也仅在 update 操作时生效。
- 对于 typeHandler，如果字段类型和设置的值类型是 equals 关系，则只需确保 typeHandler 被 MyBatis 加载到，不需要使用注解。

:::

## @Version

该注解用于标记实体类中的字段作为乐观锁版本号字段。乐观锁是一种并发控制机制，它假设多个事务可以同时进行而不会互相干扰，只在提交事务时检查是否有冲突。通过在实体类中使用`@Version`注解，MyBatis-Plus 会在更新操作时自动检查版本号，确保在更新过程中数据没有被其他事务修改。

```java
@TableName("sys_user")
public class User {
    @TableId
    private Long id;
    @TableField("nickname") // 映射到数据库字段 "nickname"
    private String name;
    private Integer age;
    private String email;
    @Version // 标记为乐观锁版本号字段
    private Integer version;
}
```

在上面的示例中，version字段被标记为乐观锁版本号。当执行更新操作时，MyBatis-Plus 会检查该字段的值是否与数据库中的值一致。如果不一致，说明在读取数据后有其他事务修改了数据，此时会抛出乐观锁异常，提示开发者进行相应的处理。

使用@Version注解可以有效地防止并发更新时出现的数据不一致问题，提高系统的并发性能和数据完整性。开发者无需手动编写版本号检查的代码，MyBatis-Plus 会自动处理这一过程。

## @EnumValue

该注解用于标记枚举类中的字段，指定在数据库中存储的枚举值。当实体类中的某个字段是枚举类型时，使用@EnumValue注解可以告诉MyBatis-Plus在数据库中存储枚举值的哪个属性。

```java
@TableName("sys_user")
public class User {
    @TableId
    private Long id;
    @TableField("nickname") // 映射到数据库字段 "nickname"
    private String name;
    private Integer age;
    private String email;
    private Gender gender; // 假设 Gender 是一个枚举类型
}

public enum Gender {
    MALE("M", "男"),
    FEMALE("F", "女");

    private String code;
    private String description;

    Gender(String code, String description) {
        this.code = code;
        this.description = description;
    }

    @EnumValue // 指定存储到数据库的枚举值为 code
    public String getCode() {
        return code;
    }
}
```

在上面的示例中，Gender枚举类中的code字段被标记为@EnumValue，这意味着在数据库中存储User实体类的gender字段时，将存储Gender枚举的code值，而不是枚举常量本身。

使用@EnumValue注解可以灵活地控制枚举类型在数据库中的存储方式，使得数据库中的数据更加紧凑和易于处理。同时，它也简化了从数据库读取枚举值时的转换过程，因为MyBatis-Plus会自动根据@EnumValue注解的配置将数据库中的值转换为对应的枚举实例。

## @TableLogic

该注解用于标记实体类中的字段作为逻辑删除字段。逻辑删除是一种数据管理策略，它不是真正地从数据库中删除记录，而是在记录中标记该记录为已删除状态。通过使用`@TableLogic`注解，MyBatis-Plus 可以在查询、更新和删除操作中自动处理逻辑删除字段的值。

```java
@TableName("sys_user")
public class User {
    @TableId
    private Long id;
    @TableField("nickname") // 映射到数据库字段 "nickname"
    private String name;
    private Integer age;
    private String email;
    @TableLogic(value = "0", delval = "1") // 逻辑删除字段
    private Integer deleted;
}
```

在上面的示例中，deleted字段被标记为逻辑删除字段。@TableLogic注解的value属性指定了逻辑未删除的值（在这个例子中是0），而delval属性指定了逻辑删除的值（在这个例子中是1）。

当执行查询操作时，MyBatis-Plus 会自动过滤掉标记为逻辑删除的记录，只返回未删除的记录。在执行更新操作时，如果更新操作会导致逻辑删除字段的值变为逻辑删除值，MyBatis-Plus 会自动将该记录标记为已删除。在执行删除操作时，MyBatis-Plus 会自动将逻辑删除字段的值更新为逻辑删除值，而不是物理删除记录。

使用@TableLogic注解可以实现数据的逻辑删除，有助于维护数据的完整性和可追溯性，同时避免了物理删除操作可能带来的数据丢失风险。开发者无需手动编写逻辑删除的代码，MyBatis-Plus 会自动处理这一过程。

## @SqlParser <Badge text="Deprecated" type="error"/>

该注解已被标记为过时（Deprecated），建议查看[@InterceptorIgnore](#interceptorignore)注解以获取更多信息。@SqlParser注解原本用于标记方法，指示在执行该方法时，MyBatis-Plus 的 SQL 解析器（用于处理如分页、逻辑删除等功能）应该被忽略，从而允许原始的 SQL 语句不被修改地执行。

```java
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    @SqlParser // 忽略 SQL 解析器，执行原始 SQL
    public List<User> selectUsers() {
        return userMapper.selectList(null);
    }
}
```

在上面的示例中，@SqlParser注解被应用于selectUsers方法，这意味着在执行该方法时，MyBatis-Plus 不会对生成的 SQL 语句进行任何额外的处理，例如添加分页信息或逻辑删除的判断。

由于@SqlParser注解已被标记为过时，建议开发者使用@InterceptorIgnore注解来替代，以实现相同的功能。@InterceptorIgnore注解可以指定忽略哪些拦截器，从而更灵活地控制 SQL 语句的处理。

**请注意**，使用过时的注解可能会导致未来的兼容性问题，因此建议查阅最新的 MyBatis-Plus 文档，了解当前推荐的最佳实践和替代方案。

## @KeySequence

该注解用于指定 Oracle 数据库中序列（Sequence）的名称，以便在实体类中生成主键值。在 Oracle 数据库中，主键通常是通过序列来生成的，而不是像其他数据库那样使用自增字段。@KeySequence注解告诉 MyBatis-Plus 使用特定的序列来生成主键。

```java
@TableName("sys_user")
@KeySequence("SEQ_USER_ID") // 指定序列名称为 "SEQ_USER_ID"
public class User {
    @TableId(type = IdType.INPUT) // 使用序列生成主键
    private Long id;
    @TableField("nickname") // 映射到数据库字段 "nickname"
    private String name;
    private Integer age;
    private String email;
}
```

在上面的示例中，@KeySequence注解被应用于实体类User，并指定了序列名称为"SEQ_USER_ID"。这意味着在插入新记录时，MyBatis-Plus 将使用这个序列来生成id字段的值。

@KeySequence注解的value属性用于指定序列的名称，而dbType属性用于指定数据库类型。如果未指定dbType，MyBatis-Plus 将默认使用注入的IKeyGenerator实现。如果有多个IKeyGenerator实现，则必须指定dbType。

使用@KeySequence注解可以确保在 Oracle 数据库中正确地生成主键值，同时简化了主键生成的配置过程。开发者无需手动编写获取序列值的代码，MyBatis-Plus 会自动处理这一过程。

## @InterceptorIgnore

该注解用于指定在执行 MyBatis-Plus 的某个方法时，是否忽略特定的拦截器。MyBatis-Plus 提供了多个拦截器，用于实现如分页、逻辑删除、多租户等功能。通过使用@InterceptorIgnore注解，开发者可以控制哪些拦截器在执行特定方法时应该被忽略。

```java
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    @InterceptorIgnore(tenantLine = "1") // 忽略多租户拦截器
    public List<User> selectUsers() {
        return userMapper.selectList(null);
    }
}
```

在上面的示例中，@InterceptorIgnore注解被应用于selectUsers方法，并指定了tenantLine属性为"1"，这意味着在执行该方法时，多租户拦截器将被忽略。

@InterceptorIgnore注解的value属性是一个字符串，可以包含多个以逗号分隔的拦截器名称。如果value的值为"1"、"yes"、"on"，则对应的拦截器将被忽略；如果value的值为"0"、"false"、"off"或为空，则拦截器将正常执行。

使用@InterceptorIgnore注解可以灵活地控制拦截器的行为，允许开发者在特定的业务场景下选择性地禁用某些拦截器，以满足特定的业务需求或性能优化。

插件使用方式详见[插件主体](/plugins)。

## @OrderBy

该注解用于指定实体类中的字段在执行查询操作时的默认排序方式。通过在实体类字段上使用@OrderBy注解，可以确保在执行查询时，如果没有显式指定排序条件，MyBatis-Plus 将按照注解中定义的排序规则返回结果。

```java
@TableName("sys_user")
public class User {
    @TableId
    private Long id;
    @TableField("nickname") // 映射到数据库字段 "nickname"
    private String name;
    @OrderBy(asc = false, sort = 10) // 指定默认排序为倒序，优先级为10
    private Integer age;
    private String email;
}
```

在上面的示例中，age字段被标记为@OrderBy，并设置了asc属性为false，表示默认排序为倒序（降序），sort属性设置为10，表示该排序规则的优先级。

@OrderBy注解的asc属性用于指定排序是否为升序，如果设置为true，则表示升序排序；如果设置为false，则表示降序排序。sort属性用于指定排序规则的优先级，数字越小，优先级越高，即越先被应用。

需要注意的是，@OrderBy注解的排序规则优先级低于在查询时通过Wrapper条件查询对象显式指定的排序条件。如果在Wrapper中指定了排序条件，那么@OrderBy注解中定义的默认排序将被覆盖。

使用@OrderBy注解可以简化代码，避免在每次查询时都显式指定排序条件，同时提供了一种默认的排序方式，有助于提高代码的可读性和维护性。
